<template>
  <div ref="surverDraw" style="border: 1px dashed gray; padding: 15px">
    <Forms />
    <!-- <div v-if="unit">物理像素参照值：{{ unit }} m / px</div>
    <div v-else style="text-align: left">
      请选择完参照物并在地图上标记参照物的大小或距离
    </div> -->
    <canvas :class="{ isTranslate: isTranslate }" ref="canvas" :width="canvasWidth" :height="canvasHeight"
      @mousedown="mousedown" @mouseup="mouseup" @mousemove="mousemove">
    </canvas>
    <!-- <button @click="exportOrPrint('export')">导出</button> -->
    <!-- <button @click="exportOrPrint('print')">打印</button> -->
  </div>
  <el-dialog title="标记文字" v-model="showTextLabelDialog" width="500" append-to-body :showClose="false">
    <el-input v-model="textLabel"></el-input>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="cancelTextLabel">取消</el-button>
        <el-button type="primary" @click="confirmTextLabel">
          确认
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import {
  computed,
  onMounted,
  onUnmounted,
  reactive,
  ref,
  unref,
  watch,
} from 'vue'
import { ElMessage } from 'element-plus'
import { v4 as uuidv4 } from 'uuid'
import { cloneDeep, throttle } from 'lodash-es'
import { handleCalcTheLine, getCanvasPosition, getLineText, drawLine, reDrawLine, reDrawCharacters, drawCharacters, drawCompassLegend } from '../../utils/config'
import { useSurveyDrawStore } from '../../store/surveyDraw'
import Forms from './form.vue'
import compass from '../../assets/compass.png'
import { infoType } from './index.data'

const surveyDrawStore = useSurveyDrawStore()
const showTextLabelDialog = ref<Boolean>(false)
const textLabel = ref<string>('')
let drawForm = computed(() => surveyDrawStore.form)

let curLineInfo: infoType = reactive({
  id: '',
  startX: 0,
  startY: 0,
  endX: 0,
  endY: 0,
  long: 0,
  translateX: 0,
  translateY: 0,
  scale: 1,
  lineWidth: 2,
  lineColor: '',
  fontColor: '',
  lineType: null,
  lineText: '',
  fontSize: 16
})
const unit = ref<number>(0)
const canvas = ref<HTMLCanvasElement | any>(null)
const compassImg = ref();
const ctx = ref<CanvasText | any>(null)
const img = ref<HTMLImageElement | any>(null)
const scale = ref<number>(1)
const translateX = ref<number>(0)
const translateY = ref<number>(0)
const startDragX = ref<number>(0)
const startDragY = ref<number>(0)
const drawFlag = ref<Boolean>(false)
const isWheel = ref<Boolean>(false)
const isKeyMeta = ref<Boolean>(false)
const isTranslate = ref<Boolean>(false)
const isDragging = ref<Boolean>(false)
const lineList = ref<any[]>([])
const props = defineProps({
  canvasImg: {
    type: String,
    require: true,
  },
  canvasWidth: {
    type: String,
    default: '800'
  },
  canvasHeight: {
    type: String,
    default: '400'
  }
})
watch(
  () => scale.value,
  (newValue) => {
    if (newValue === 1) {
      translateX.value = 0
      translateY.value = 0
      startDragX.value = 0
      startDragY.value = 0
      redraw()
    }
  }
)
// 初始化图片、事件监听
const initCanvas = () => {
  ctx.value = unref(canvas).getContext('2d')
  img.value = new Image() // 创建一个<img>元素
  img.value.src = props.canvasImg // 设置图片源地址

  // 初始化指北针图片
  compassImg.value = new Image()
  compassImg.value.src = compass
  compassImg.value.onload = () => {
    compassImg.value.width = 60
    compassImg.value.height = 60
  }

  // 图片加载完成后执行绘制
  img.value.onload = () => {
    // 根据图片宽高比，计算canvas高度
    const { width: imgWidth, height: imgHeight } = img.value
    img.value.width = unref(canvas).width
    img.value.height = (unref(canvas).width / imgWidth) * imgHeight
    redraw()
  }

  // 事件监听
  canvas.value.addEventListener('wheel', listenerWheel)
  canvas.value.addEventListener('contextmenu', listenerMenu)
  addEventListener('keydown', keydown)
  addEventListener('keyup', keyup)
}
// 销毁事件监听
const destoryMethods = () => {
  unref(canvas).removeEventListener('wheel', listenerWheel)
  unref(canvas).removeEventListener('contextMenu', listenerMenu)
  removeEventListener('keydown', keydown)
  removeEventListener('keyup', keyup)
}
// 鼠标按下
const mousedown = (e: MouseEvent) => {
  if (unref(isTranslate)) {
    startDragX.value = e.clientX - unref(translateX)
    startDragY.value = e.clientY - unref(translateY)
    isDragging.value = true
    return
  }
  if (!unref(drawForm).referenceType) {
    message.info('请先选择参照物再进行量尺')
    return
  }
  drawFlag.value = true
  // 存储起始坐标
  saveCanvasPosition(e)
}
// 鼠标抬起
const mouseup = () => {
  if (unref(isTranslate)) {
    isDragging.value = false
    return
  }
  drawFlag.value = false
  setTimeout(() => {
    if (lineList.value.length > 0 && lineList.value[unref(lineList).length - 1]?.lineType === 'arrow') {
      showTextLabelDialog.value = true
    }
  }, 300)
}
// 鼠标拖动
const mousemove = (e: MouseEvent) => {
  if (unref(isTranslate) && unref(isDragging)) {
    reTranslate(e)
    return
  }
  drawRect(e)
}
// 键盘按下
const keydown = (e: KeyboardEvent) => {
  console.log(e.code, '键盘按下')
  if (e.code === 'Space' && !unref(drawFlag)) {
    e.preventDefault()
    isTranslate.value = true
  } else {
    isTranslate.value = false
  }
  if (e.code === 'MetaLeft') {
    isKeyMeta.value = true
  } else if (unref(isKeyMeta) && e.code === 'KeyZ' && !unref(showTextLabelDialog)) {
    // 撤销、重新绘制
    lineList.value.pop()
    unref(lineList).length ? handleReRender() : redraw()
  }
}
// 键盘抬起
const keyup = (e: KeyboardEvent) => {
  console.log(e.code, '键盘抬起')
  if (e.code === 'Space') {
    isTranslate.value = false
  }
  if (e.code === 'MetaLeft') {
    isKeyMeta.value = false
  }
}
// 计算坐标，存储鼠标按下、滑动的坐标
const saveCanvasPosition = (e: MouseEvent, type = 'start') => {
  const pt = getCanvasPosition(
    unref(canvas),
    unref(scale),
    e.clientX,
    e.clientY
  )
  if (type === 'start') {
    curLineInfo.startX = pt.x
    curLineInfo.startY = pt.y
  } else {
    curLineInfo.endX = pt.x
    curLineInfo.endY = pt.y
  }
}
// 绘制
const drawRect = (e: MouseEvent) => {
  isWheel.value = false
  if (unref(drawFlag)) {
    saveCanvasPosition(e, 'end')
    curLineInfo.long = handleCalcTheLine(curLineInfo)
    // 将translateX、translateY存储，在重新绘制时，添加相反的偏移量*缩放
    curLineInfo.translateX = -unref(translateX)
    curLineInfo.translateY = -unref(translateY)
    curLineInfo.scale = unref(scale)
    curLineInfo.lineText = getLineText(curLineInfo, unit.value)
    Object.assign(curLineInfo, unref(drawForm))
    redraw()
    handleReRender()

    // 在变换后的坐标系中绘制
    unref(ctx).save()
    unref(ctx).translate(unref(canvas).width / 2, unref(canvas).height / 2)
    unref(ctx).scale(unref(scale), unref(scale))

    // 绘制线段
    drawLine(ctx.value, curLineInfo)

    // 文字标注不绘制距离
    if (unref(unit) && curLineInfo.lineType !== 'arrow') {
      drawCharacters(unref(ctx), curLineInfo)
    }

    unref(ctx).restore()
  } else {
    // 判断是否已经设置好参照物算出物理像素参考值
    if (!unref(unit) && curLineInfo.startX) {
      if (
        curLineInfo.startX === curLineInfo.endX &&
        curLineInfo.startY === curLineInfo.endY
      ) {
        return
      }
      unit.value =
        unref(drawForm).referenceType / handleCalcTheLine(curLineInfo)
      redraw()
    } else {
      if (curLineInfo.startX && curLineInfo.endY) {
        let lineInfo = cloneDeep(curLineInfo)
        pushItem(lineInfo)
      }
    }
    curLineInfo = reactive({
      id: '',
      startX: 0,
      startY: 0,
      endX: 0,
      endY: 0,
      long: 0,
      translateX: 0,
      translateY: 0,
      scale: 1,
      lineWidth: 2,
      lineColor: '',
      fontColor: '',
      lineType: null,
      lineText: '',
      fontSize: 16
    })
  }
}
// 线段推入列表
const pushItem = throttle((item: infoType) => {
  item.long = handleCalcTheLine(item)
  lineList.value.push(item)
}, 300)

// 重绘
const handleReRender = () => {
  if (unref(lineList).length > 0) {
    if (!unref(isWheel) && !unref(isDragging)) {
      redraw()
    }

    unref(ctx).save()
    unref(ctx).translate(
      unref(canvas).width / 2 + unref(translateX),
      unref(canvas).height / 2 + unref(translateY)
    )
    unref(ctx).scale(unref(scale), unref(scale))

    unref(lineList).forEach((item: infoType) => {
      // 绘制线段
      reDrawLine(ctx.value, item)
      // 绘制文字
      reDrawCharacters(ctx.value, item)
    })

    unref(ctx).restore()
  }
}

// 重新绘制图片
const redraw = () => {
  unref(ctx).clearRect(0, 0, unref(canvas).width, unref(canvas).height)
  // 保存当前画布状态
  unref(ctx).save()
  unref(ctx).setTransform(1, 0, 0, 1, 0, 0) // 重置矩阵
  // 移动坐标系原点至画布中心
  unref(ctx).translate(
    unref(canvas).width / 2 + unref(translateX),
    unref(canvas).height / 2 + unref(translateY)
  )
  // 应用缩放变换
  unref(ctx).scale(unref(scale), unref(scale))
  // 绘制图片（需确保image已加载）
  if (unref(img)) {
    unref(ctx).drawImage(
      unref(img),
      -unref(img).width / 2,
      -unref(img).height / 2,
      unref(img).width,
      unref(img).height
    )
  }

  // 绘制指北针图例
  if (unref(compassImg)) {
    drawCompassLegend(
      unref(ctx),
      unref(canvas),
      unref(compassImg),
      unref(translateX),
      unref(translateY),
      unref(scale),
      unref(unit)
    )
  }

  // 恢复原始坐标系
  unref(ctx).restore()

  if (unref(isWheel) || unref(isDragging)) {
    handleReRender()
  }
}
// 计算移动距离
const reTranslate = (e: WheelEvent | MouseEvent) => {
  // 计算新的位置
  let newTranslateX = e.clientX - unref(startDragX)
  let newTranslateY = e.clientY - unref(startDragY)

  // 计算缩放后的图片尺寸
  const scaledImgWidth = unref(img).width * unref(scale)
  const scaledImgHeight = unref(img).height * unref(scale)

  // 计算最大可移动范围
  const maxTranslateX = (scaledImgWidth - unref(canvas).width) / 2
  const maxTranslateY = (scaledImgHeight - unref(canvas).height) / 2

  // 限制移动范围
  translateX.value = Math.min(
    Math.max(newTranslateX, -maxTranslateX),
    maxTranslateX
  )
  translateY.value = Math.min(
    Math.max(newTranslateY, -maxTranslateY),
    maxTranslateY
  )
  redraw()
}
// 滚轮缩放canvas
const listenerWheel = (e: WheelEvent) => {
  isWheel.value = true
  e.preventDefault()
  // 最大缩放; 1; 最小缩放; 0.1;
  const MAX_SCALE = 5
  const MIN_SCALE = 1
  const delta = e.deltaY < 0 ? 0.1 : -0.1
  scale.value = Math.min(MAX_SCALE, Math.max(MIN_SCALE, unref(scale) + delta))
  if (unref(translateX) !== 0 && unref(translateY) !== 0) {
    // 存在偏移量，限制偏移范围
    reTranslate(e)
  } else {
    redraw()
  }
}

// 禁用右键菜单
const listenerMenu = (e: CustomEvent) => {
  e.preventDefault()
}
// 导出、打印
// const exportOrPrint = (type) => {
//   type === 'export' ? exportPdf(unref(canvas)) : printPdf(unref(canvas))
// }
// 文字标记取消事件
const cancelTextLabel = () => {
  showTextLabelDialog.value = false
  lineList.value.pop()
  unref(lineList).length ? handleReRender() : redraw()
}
// 文字标记确认事件
const confirmTextLabel = () => {
  if (!textLabel.value.trim()) {
    message.info('请填写标记文字')
    return;
  }
  showTextLabelDialog.value = false
  lineList.value[unref(lineList).length - 1].lineText = textLabel.value
  textLabel.value = ''
  // 重新绘制
  handleReRender()
}
onMounted(() => {
  initCanvas()
})
onUnmounted(() => {
  destoryMethods()
})
</script>

<style scoped>
.isTranslate {
  cursor: grab;
}
</style>
